﻿using Microsoft.Reporting.WinForms;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Install-Package Microsoft.ReportingServices.ReportViewerControl.WinForms -Pre 控制台安装
namespace RDLC_Report
{
    public struct ReportRenderOut
    {
        /// <summary>
        /// 警告信息
        /// </summary>
        public Microsoft.Reporting.WinForms.Warning[] Warnings;
        public string[] Streamids;
        /// <summary>
        /// 文件头类型
        /// </summary>
        public string MimeType;
        /// <summary>
        /// 编码格式
        /// </summary>
        public string Encoding;
        /// <summary>
        /// 文件后缀名
        /// </summary>
        public string Extension;
    }
    /// <summary>
    /// 页面打印的宽高，边距(单位 cm\in) 允许为空
    /// </summary>
    public class ReportDeviceInfo
    {
        /// <summary>
        /// 页面宽度 (单位 cm\in)
        /// </summary>
        public string PageWidth { get; set; }// = "8.5in";
        /// <summary>
        /// 页面高度 (单位 cm\in)
        /// </summary>
        public string PageHeight { get; set; }// = "8.27in";
        /// <summary>
        /// 左边距 (单位 cm\in)
        /// </summary>
        public string MarginLeft { get; set; } //= "0.2in";
        /// <summary>
        /// 右边距 (单位 cm\in)
        /// </summary>
        public string MarginRight { get; set; }// = "0.2in";
        /// <summary>
        /// 上边距 (单位 cm\in)
        /// </summary>
        public string MarginTop { get; set; } //= "0.2in";
        /// <summary>
        /// 下边距 (单位 cm\in)
        /// </summary>
        public string MarginBottom { get; set; }// = "0.2in";
    }
    public class ReportRDLC:IDisposable
    {
        #region 公共属性
        /// <summary>
        /// 可生成的文件类型
        /// </summary>
        public enum FileType { Excel, PDF, Word, Image }
        /// <summary>
        /// *必填属性 模板的绝对路径 C:\TEST\REPORT.rdlc
        /// </summary>
        public string RdlcPath { get; set; }
        /// <summary>
        /// *必填属性 文件生成后的保存路径包括文件名,不需要后缀名（.xxx）,后缀名有代码根据SaveType自动匹配（eg:保存成名字为"MYFILE"的文件 C:\TEST\FILE\MYFILE）
        /// </summary>
        public string SavePath { get; set; }
        /// <summary>
        /// *必填属性 需要生成的文件类型
        /// </summary>
        public FileType SaveType { get; set; }
        /// <summary>
        /// *可选属性，假如你的rdlc模板不需要数据源  数据源 key对应数据集在模板里面的数据集名字，value对应数据列表，一个rdlc允许有多个数据源
        /// </summary>
        public Dictionary<string, object> DataSources { get; set; }
        /// <summary>
        /// *可选属性，假如你的rdlc模板不需要自定义参数  报表自定义参数 可以定义字符串 数字 布尔 直接把某个字段传到报表里面使用
        /// </summary>
        public List<ReportParameter> ReportParams { get; set; }
        /// <summary>
        /// *可选属性 不设，使用默认 页面大小格式设置
        /// </summary>
        public ReportDeviceInfo DeviceInfo { get; set; } = new ReportDeviceInfo();
        /// <summary>
        /// rdlc报表生成字节流时的一些输出参数，执行GetStream(),SaveAsFile()方法后会进行赋值
        /// </summary>
        public ReportRenderOut RenderOut;
        #endregion
        public ReportRDLC(string rdlcPath, string savePaht, FileType saveType)
        {
            RdlcPath = rdlcPath;
            SavePath = savePaht;
            SaveType = saveType;
        }
        #region 公共方法
        /// <summary>
        /// 读取本地rdlc模板，填充数据集和自定义的参数
        /// </summary>
        /// <returns>LocalReport</returns>
        public LocalReport GetLocalReport()
        {
            LocalReport localReport = new LocalReport();
            localReport.EnableExternalImages = true;//设为允许外部资源，不然无法加载图片
            //localReport.DataSources.Clear();
            localReport.ReportPath = RdlcPath;
            if (ReportParams != null) localReport.SetParameters(ReportParams);
            //循环将数据集名字和数据源赋给报表主体对象 
            if (DataSources != null) { foreach (var kvp in DataSources) { localReport.DataSources.Add(new ReportDataSource(kvp.Key, kvp.Value)); } }
            return localReport;
        }
        /// <summary>
        /// 根据SaveType，DeviceInfo，DataSources，ReportParams 返回字节流
        /// </summary>
        /// <returns></returns>
        public byte[] GetStream()
        {
            LocalReport localReport = GetLocalReport();
            try
            {
                //localReport.Refresh();
                string typeName = Enum.GetName(typeof(FileType), SaveType);
                string deviceInfo = $@"<DeviceInfo>
                                        <OutputFormat></OutputFormat>
                                        <PageWidth>{DeviceInfo.PageWidth ?? ""}</PageWidth>
                                        <PageHeight>{DeviceInfo.PageHeight ?? ""}</PageHeight>
                                        <MarginTop>{DeviceInfo.MarginTop ?? ""}</MarginTop>
                                        <MarginLeft>{DeviceInfo.MarginLeft ?? ""}</MarginLeft>
                                        <MarginRight>{DeviceInfo.MarginRight ?? ""}</MarginRight>
                                        <MarginBottom>{DeviceInfo.MarginBottom ?? ""}</MarginBottom>
                                       </DeviceInfo>";
                //Render方法介绍链接 https://docs.microsoft.com/zh-cn/previous-versions/ms252172(v=vs.140)
                //format Excel、PDF、Word 和 Image
                byte[] bytes = localReport.Render(typeName, deviceInfo, out RenderOut.MimeType, out RenderOut.Encoding, out RenderOut.Extension, out RenderOut.Streamids, out RenderOut.Warnings);
                return bytes;
            }
            catch(Exception ex)
            {
#if DEBUG
                throw ex;
#endif               
                return null;
            }
            finally
            {                
                localReport.Dispose();
                localReport.ReleaseSandboxAppDomain();
                //System.GC.Collect();
            }
        }
        /// <summary>
        /// 根据SavePath，SaveType生成文件保存到硬盘
        /// </summary>
        /// <returns>返回保存结果</returns>
        public bool SaveAsFile()
        {
            var stream = GetStream();
            if (stream == null || stream.Length == 0) return false;
            var file = $"{SavePath}.{RenderOut.Extension}";
            var folder = Path.GetDirectoryName(file);
            if (!Directory.Exists(folder))//如果文件夹不存在则创建
            {
                Directory.CreateDirectory(folder);
            }
            using (FileStream fs = new FileStream(file, FileMode.Create))
            {
                fs.Write(stream, 0, stream.Length);
                fs.Close();
            }
            return true;
        }
        #endregion

        public static string GetMd5(string md5string)
        {
            byte[] testEncrypt = System.Text.Encoding.UTF8.GetBytes(md5string);
            //获取加密服务  
            System.Security.Cryptography.MD5CryptoServiceProvider md5CSP = new System.Security.Cryptography.MD5CryptoServiceProvider();
            //加密Byte[]数组  
            byte[] resultEncrypt = md5CSP.ComputeHash(testEncrypt);
            StringBuilder sub = new StringBuilder();
            for (int i = 0; i < resultEncrypt.Length; i++)
            {
                sub.Append(resultEncrypt[i].ToString("x2"));
            }

            return sub.ToString().ToUpper();
        }

        public void Dispose()
        {
            DataSources = null;
            ReportParams = null;
        }
    }
}
